package com.sig.common.util;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Spring容器实例工具
 *
 * @author sig
 * @since 2019/2/22
 */
@Service
public class SpringUtil implements ApplicationContextAware {
    /** Spring容器是否进入关闭状态 */
    private volatile boolean destroy = false;
    /** Spring容器是否初始完成状态 */
    private volatile boolean init = false;

    /** Spring应用上下文环境 */
    private static ApplicationContext applicationContext;

    private final static Map<Class<?>, Object> types = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
        SpringUtil.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 非Spring环境下使用
     *
     * @param clazz
     * @param bean
     */
    public static void register(Class<?> clazz, Object bean) {
        types.put(clazz, bean);
    }

    /**
     * @param name
     * @return
     */
    public static Object getBean(final String name) {
        return applicationContext == null ? null : applicationContext.getBean(name);
    }

    /**
     * @param requireType
     * @param <T>
     * @return
     */
    public static <T> T getBean(final Class<T> requireType) {
        return getBean(requireType.getSimpleName(), requireType);
    }

    /**
     * @param name
     * @param requireType
     * @param <T>
     * @return
     */
    public static <T> T getBean(final String name, final Class<T> requireType) {
        return applicationContext == null ? null : applicationContext.getBean(name, requireType);
    }

    /**
     * @param type
     * @return
     */
    public static String[] getBeanNamesOfType(final Class<?> type) {
        return applicationContext == null ? ArrayUtils.EMPTY_STRING_ARRAY : applicationContext.getBeanNamesForType(type);
    }

    /**
     * @param type
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBeanOfType(final Class<T> type) {
        Object bean = types.get(type);
        if (bean != null)
            return (T) bean;
        if (applicationContext == null)
            return null;
        String[] names = getBeanNamesOfType(type);
        if (ArrayUtils.isEmpty(names))
            return null;
        T t = getBean(names[0], type);
        types.put(type, t);
        return t;
    }

    /**
     * @param type
     * @param <T>
     * @return
     */
    public static <T> Map<String, T> getBeansOfType(final Class<T> type) {
        return applicationContext == null ? Collections.emptyMap() : applicationContext.getBeansOfType(type);
    }

    public boolean isDestroy() {
        return destroy;
    }

    public void destroy() {
        destroy = true;
    }

    public boolean isInit() {
        return init;
    }

    public void init() {
        init = true;
    }

    public boolean running() {
        return !destroy && init;
    }
}
